package city.spring.modules.system.service.impl;

import city.spring.modules.ext.EntityExtUtils;
import city.spring.modules.ext.entity.OrganizationPermissionExt;
import city.spring.modules.ext.entity.OrganizationRoleExt;
import city.spring.modules.ext.service.OrganizationPermissionExtService;
import city.spring.modules.ext.service.OrganizationRoleExtService;
import city.spring.modules.system.entity.OrganizationEntity;
import city.spring.modules.system.entity.PermissionEntity;
import city.spring.modules.system.entity.RoleEntity;
import city.spring.modules.system.repository.OrganizationRepository;
import city.spring.modules.system.service.OrganizationService;
import city.spring.modules.system.service.PermissionService;
import city.spring.modules.system.service.RoleService;
import city.spring.utils.TreeDataUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.ListMultimap;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Service：组织机构
 *
 * @author 侯坤林
 * @date 2020-05-30 14:06:37
 */
@CacheConfig(cacheNames = {OrganizationServiceImpl.CACHE_NAME})
@Transactional(rollbackFor = Throwable.class)
@Service
public class OrganizationServiceImpl extends ServiceImpl<OrganizationRepository, OrganizationEntity> implements OrganizationService {
    private final RoleService roleService;
    private final PermissionService permissionService;
    private final OrganizationRoleExtService organizationRoleExtService;
    private final OrganizationPermissionExtService organizationPermissionExtService;

    public OrganizationServiceImpl(RoleService roleService, PermissionService permissionService, OrganizationRoleExtService organizationRoleExtService, OrganizationPermissionExtService organizationPermissionExtService) {
        this.roleService = roleService;
        this.permissionService = permissionService;
        this.organizationRoleExtService = organizationRoleExtService;
        this.organizationPermissionExtService = organizationPermissionExtService;
    }

    @Override
    public void saveOrganization(OrganizationEntity entity) {
        if (!save(entity)) {
            throw new RuntimeException("保存信息失败");
        }
    }

    @Override
    public void updateOrganization(OrganizationEntity entity) {
        // 使用 updateById(entity); 修改数据时，将会修改 entity 对象中所有非null数据，如果某个字段为null，将会忽略该字段的修改
        if (entity.getId().equals(entity.getPid())) {
            throw new RuntimeException("当前对象的父级信息错误，不能与当前对象相同");
        }
        boolean update = updateById(entity);
        if (!update) {
            throw new RuntimeException("修改信息失败");
        }
    }

    @Override
    public void deleteOrganization(String primaryKey) {
        removeById(primaryKey);
        organizationRoleExtService.lambdaUpdate().eq(OrganizationRoleExt::getOrgId, primaryKey).remove();
        organizationPermissionExtService.lambdaUpdate().eq(OrganizationPermissionExt::getOrgId, primaryKey).remove();
    }

    @Override
    public void deleteOrganization(List<String> primaryKeys) {
        removeByIds(primaryKeys);
        organizationRoleExtService.lambdaUpdate().in(OrganizationRoleExt::getOrgId, primaryKeys).remove();
        organizationPermissionExtService.lambdaUpdate().in(OrganizationPermissionExt::getOrgId, primaryKeys).remove();
    }

    @Override
    public List<OrganizationEntity> converterToTreeDatasource(@NonNull List<OrganizationEntity> rawDatasource, boolean loadParentEntity) {
        ListMultimap<String, OrganizationEntity> multimap;
        // 对整体进行排序
        rawDatasource.sort(Comparator.comparing(OrganizationEntity::getGmtCreate));
        multimap = TreeDataUtils.handlerTreeDatasource(rawDatasource, "",
                OrganizationEntity::getId, OrganizationEntity::getPid,
                OrganizationEntity::getChildren, OrganizationEntity::setChildren);
        if (loadParentEntity) {
            return TreeDataUtils.loadTreeParents(multimap, "", lambdaQuery(),
                    OrganizationEntity::getId, OrganizationEntity::setChildren);
        }
        return new ArrayList<>(multimap.values());
    }

    @Override
    public void loadPermissions(OrganizationEntity entity) {
        if (entity == null) {
            return;
        }
        entity.setPermissions(permissionService.getOrganizationPermissions(entity.getId()));
    }

    @Override
    public void loadPermissions(List<OrganizationEntity> entities) {
        entities.forEach(this::loadPermissions);
    }

    @Override
    public void loadRoles(OrganizationEntity entity) {
        if (entity == null) {
            return;
        }
        entity.setRoles(roleService.getOrganizationRolesWithAuthority(entity.getId()));
    }

    @Override
    public void loadRoles(List<OrganizationEntity> entities) {
        entities.forEach(this::loadRoles);
    }

    @Override
    public OrganizationEntity getOrganizationInfo(String primaryKey, boolean loadPermissions, boolean loadRoles) {
        OrganizationEntity entity = getById(primaryKey);
        if (loadPermissions) {
            loadPermissions(entity);
        }
        if (loadRoles) {
            loadRoles(entity);
        }
        return entity;
    }

    @Override
    public void setOrganizationRoles(OrganizationEntity entity) {

        EntityExtUtils.repairRelation(organizationRoleExtService, entity,
                OrganizationEntity::getId, OrganizationEntity::getRoles, RoleEntity::getId,
                OrganizationRoleExt::new, OrganizationRoleExt::getOrgId);
    }

    @Override
    public void setOrganizationPermissions(OrganizationEntity entity) {
        EntityExtUtils.repairRelation(organizationPermissionExtService, entity,
                OrganizationEntity::getId, OrganizationEntity::getPermissions, PermissionEntity::getId,
                OrganizationPermissionExt::new, OrganizationPermissionExt::getOrgId);
    }

    @Override
    public Set<String> getChildrenIds(Set<String> parentIds) {
        Set<String> ids = new HashSet<>();
        if (parentIds.isEmpty()) {
            return ids;
        }
        List<OrganizationEntity> list = lambdaQuery()
                .in(OrganizationEntity::getPid, parentIds)
                .list();
        if (list.isEmpty()) {
            return ids;
        }
        Set<String> childrenIds = list.stream().map(OrganizationEntity::getId).collect(Collectors.toSet());
        ids.addAll(childrenIds);
        ids.addAll(getChildrenIds(childrenIds));

        return ids;
    }
}